#include "vfx_lighting.h"
#include "graphics/graphics_util.h"
#include "graphics/graphics.h"
#include "dd/dd_man.h"
#include "script/scripthelper.h"
#include "script/write_lua.h"

#include "num/rand.h"
#include "util/timer.h"				// log
#include "util/log.h"				// log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    

#include <math.h>

using namespace vfx;
using namespace script;

VFXLight::VFXLight(LightingScene* pScene) :
	m_pScene(pScene),
	m_IsActive(true),
	m_Color(0xFFFFFFFF),
	m_Flicker(false),
	m_FlickerSpeed(1),
	m_FlickerSize(1),
	m_ShouldExport(true),
	m_Scale(1),
	m_CanResize(true)
{
	m_TimerOffset = num::RAND32::Instance().RandFloat(0, 20);
}

VFXLight::~VFXLight()
{

}


void	VFXLight::Setup(const std::string& File, const math::Vec2& Pos, COLOR c, bool CanResize)
{
	m_Pos = Pos;
	m_Color =c;
	m_File = File;
	m_Sprite.Create(File);
	m_Sprite.GetScreenSize(m_Size);
	m_CanResize = CanResize;
	//m_Sprite.m_Additive = true;
}

bool	VFXLight::IsVisible(const math::Vec2& Offset, float Scale)
{
	if (!m_IsActive) return false;
	float	sx = (m_Size.x*0.5f*m_Scale) ;
	float	sy = (m_Size.y*0.5f*m_Scale) ;
	if ((m_Pos.x + sx - Offset.x) * Scale < 0) return false;		// left
	if ((m_Pos.y + sy - Offset.y) * Scale < 0) return false;		// above

	if (((m_Pos.x - sx) - Offset.x) * Scale > 1) return false;		// right
	if (((m_Pos.y - sy) - Offset.y) * Scale > 1) return false;		// below

	return true;
}



void	VFXLight::Render(const math::Vec2& Offset, float Scale)
{
	if (!m_IsActive) return;
	if (m_Flicker)
	{
		m_Sprite.m_Size = Scale*(m_Size + (m_Size* sinf((m_TimerOffset+TIMER().Time()) * m_FlickerSpeed) * m_FlickerSize))*m_Scale;
	} else
	{
		m_Sprite.m_Size = Scale*m_Size*m_Scale;
	}
	m_Sprite.m_Color = m_Color;
	m_Sprite.m_Pos = ((m_Pos - Offset) - (m_Sprite.m_Size*0.5f)) * Scale;
	m_Sprite.RenderClipped(math::Vec2(0,0), math::Vec2(1,1));
}
void	VFXLight::RenderDebug(const math::Vec2& Offset, float Scale)
{
	if (!m_IsActive) return;
	if (m_Flicker)
	{
		m_Sprite.m_Size = Scale*(m_Size + (m_Size* sinf((m_TimerOffset+TIMER().Time()) * m_FlickerSpeed) * m_FlickerSize))*m_Scale;
	} else
	{
		m_Sprite.m_Size = Scale*m_Size*m_Scale;
	}
	m_Sprite.m_Color = ColorRGBA(255, 255, 255, 100); //m_Color;
	m_Sprite.m_Pos = Scale*(m_Pos - Offset);

	// borrow that code
	math::Vec2	Size = m_Sprite.m_Size * 0.5f;
	dd::Manager::Instance().AddBox2D(m_Sprite.m_Pos-Size , m_Sprite.m_Pos +  Size, m_Sprite.m_Color);
}

math::Vec4	VFXLight::GetColor()
{
	math::Vec4	Col;
	Col.x = ColorGetR(m_Color) / 255.0f;
	Col.y = ColorGetG(m_Color) / 255.0f;
	Col.z = ColorGetB(m_Color) / 255.0f;
	Col.w = ColorGetA(m_Color) / 255.0f;
	return Col;
}

////////////////////////////////////////////////////////////////////////////////////

LightingScene::LightingScene() :
	m_pCache(0),
	m_Width(1024), m_Height(1024), m_Depth(8),
	m_ClearColor()
{
	m_ClearColor = ColorRGBA(9, 9, 9, 255);
}
LightingScene::~LightingScene()
{
	UTIL_SAFE_DELETE(m_pCache);
	for (unsigned int i=0; i<m_Lights.size(); i++)
	{
		VFXLight*		pCurrent = m_Lights[i];
		delete pCurrent;
	}
	m_Lights.resize(0);
}
void	LightingScene::Init(int	Width , int Height , int Depth )
{
	m_Width = Width;
	m_Height = Height;
	m_Depth = Depth;

	m_pCache = GetGraphics()->CreateCache(Width, Height, Depth);

	m_pCache->BeginCache();
	m_pCache->ClearCache(ColorRGBA(0,0,0,255));
	m_pCache->EndCache();

}
void	LightingScene::Refresh(const math::Vec2& Offset, float	Scale)
{
	if (!m_pCache) return;
	Graphics* pG = GetGraphics();

	if (m_Lights.empty()) return;
	int		Index = 0;
	m_pCache->BeginCache();
	m_pCache->ClearCache(m_ClearColor);

	pG->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_SRC_ALPHA );
	pG->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_INVSRC_ALPHA );
	pG->SetRenderState(Graphics::RS_ALPHA_TEST, false);

	for (unsigned int i=0; i<m_Lights.size(); i++)
	{
		VFXLight*		pLight = m_Lights[i];
		if (!pLight->IsVisible(Offset, Scale)) continue;		// not visible
		pLight->Render(Offset, Scale);
	}

	pG->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_SRC_ALPHA );
	pG->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_INVSRC_ALPHA );
	m_pCache->EndCache();
}

void	LightingScene::RenderDebug(const math::Vec2& Offset, float	Scale)
{
	for (unsigned int i=0; i<m_Lights.size(); i++)
	{
		VFXLight*		pLight = m_Lights[i];
		if (!pLight->IsVisible(Offset, Scale)) continue;		// not visible
		pLight->RenderDebug(Offset, Scale);
	}
}

void	LightingScene::Render()
{

	math::Vec2	Start(0,0);
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();

#ifdef _ENABLE_OPENGL_
	// OpenGL textures are upside down; faster to handle it as a tex-coord switch than to swizzle it at runtime
	math::Vec2	End(GraphicsUtil::WIDTH / (float)m_Width, GraphicsUtil::HEIGHT / (float)-m_Height); 
#else
	math::Vec2	End(GraphicsUtil::WIDTH / (float)m_Width, GraphicsUtil::HEIGHT / (float)m_Height); 
#endif
	
	int		Index = GraphicsUtil::FillQuad(math::Vec2(-0.0,-0.0f), math::Vec2(1.0,1.0), Start, End, ColorRGBA(255, 255, 255, 255), VertA);	
	m_pCache->SetTextureStage(0);

	Graphics* g = GetGraphics();

	g->SetTexStage(0, Graphics::COLOR_OP, Graphics::BLEND_TEXTUREALPHA);
	g->SetRenderState( Graphics::RS_ALPHA_TEST, 0);
	g->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_DEST );
	g->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_SRC);	

	GetGraphics()->DrawVertex2D(VertA, Index);

	g->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_SRC_ALPHA );
	g->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_INVSRC_ALPHA );		
	g->SetRenderState( Graphics::RS_ALPHA_TEST, 1);
	g->SetTexStage(0, Graphics::COLOR_OP, Graphics::BLEND_TEXTUREALPHA );

}
VFXLight*	LightingScene::CreateLight(const std::string& LightImg, const math::Vec2& Pos, COLOR c, bool CanResize )
{
	VFXLight*	pLight = new VFXLight(this);
/*	float	R = ColorGetRf(c);
	float	G = ColorGetGf(c);
	float	B = ColorGetBf(c);
	float	A = ColorGetAf(c);

	UTIL_CLAMP(R, 0, 0.7f);
	UTIL_CLAMP(G, 0, 0.7f);
	UTIL_CLAMP(B, 0, 0.7f);
	UTIL_CLAMP(A, 0, 0.7f);
	pLight->Setup(LightImg, Pos, ColorfRGBA(R, G, B, A));*/
	pLight->Setup(LightImg, Pos, c, CanResize);
	m_Lights.push_back(pLight);
	return pLight;
}

void		LightingScene::DropLight(VFXLight* pLight)
{
	for (unsigned int i=0; i<m_Lights.size(); i++)
	{
		VFXLight*		pCurrent = m_Lights[i];
		if (pCurrent==pLight)
		{
			delete pCurrent;
			m_Lights.erase(m_Lights.begin()+i);
			return;
		}
	}
}

void	LightingScene::SaveFile(script::WriteLua& WL, bool CloseComma )
{
	WL.StartTable("GameLights");
		for (unsigned int i=0; i<m_Lights.size(); i++)
		{
			VFXLight*		pCurrent = m_Lights[i];
			if (!pCurrent->ShouldExport()) continue;
			WL.StartTable();
				WL.WritePair("Pos", pCurrent->GetPos());
				WL.WritePair("File", pCurrent->GetFile());
				WL.WritePair("Color", pCurrent->GetColor());
				WL.WritePair("ShouldFlicker", pCurrent->ShouldFlicker());
				WL.WritePair("FlickerSpeed", pCurrent->GetFlickerSpeed());
				WL.WritePair("FlickerAmp", pCurrent->GetFlickerAmp());
			WL.CloseTable(true);
		}
	WL.CloseTable(CloseComma);
}

void	LightingScene::LoadFile(LuaPlus::LuaState& Obj)
{
	LuaPlus::LuaObject	Table = Obj.GetGlobal("GameLights");
	if (!Table.IsTable()) return;

	for (int i=0; i<Table.GetTableCount(); i++)
	{
		LuaPlus::LuaObject	Light = Table.GetByIndex(i+1);
		if (!Light.IsTable()) continue;

		math::Vec2	Pos		= ScriptHelper::GetTableVec2(Light, "Pos");
		std::string	File	= ScriptHelper::GetTableString(Light, "File");
		math::Vec4	Color	= ScriptHelper::GetTableVec4(Light, "Color", math::Vec4(1,1,1,1));
		float		Scale	= ScriptHelper::GetTableFloat(Light, "Scale");
        bool		ShouldFlicker	= ScriptHelper::GetTableBool(Light, "PosShouldFlicker", false);
		float		FlickerSpeed	= ScriptHelper::GetTableFloat(Light, "FlickerSpeed");
		float		FlickerAmp	= ScriptHelper::GetTableFloat(Light, "FlickerAmp");

		VFXLight*	pLight = CreateLight(File, Pos, ColorfRGBA(Color.x,Color.y,Color.z,Color.w));
		if (ShouldFlicker)
			pLight->SetFlicker(FlickerSpeed, FlickerAmp);

		pLight->SetScale(Scale);
	}
}


